package dfsketches.gr3d;

import java.util.Hashtable;

import dfsketches.terrain_generator.CosineGenerator;
import dfsketches.terrain_generator.MountainedGenerator;
import dfsketches.terrain_generator.TerrainGenerator;
import dfsketches.terrain_generator.Window;
import dfsketches.visualizer_2d.ColorSolver;

public class World {
	
	private Scene sc;
	private Player player;
	private Geodata geodata;
	
	private Hashtable<Long, MovableObject> nToMobs;
	
	public World(Scene scene) {

		/*
		 * Worldgen
		 */
		Hashtable<String, Object> worldgenParams = new Hashtable<String, Object>();
		worldgenParams.put("height", 64f);
		worldgenParams.put("partition", 128);
		worldgenParams.put("init_height", -100f);
		worldgenParams.put("magnitude", 100f);
		worldgenParams.put("fixed_border", false);
		worldgenParams.put("magnitude_k", 2.1f);
		worldgenParams.put("mountain_k", 1f);

		TerrainGenerator tg = new MountainedGenerator();
		int[][] tmap = tg.generate(256, worldgenParams);
//		tmap = (new Erosion()).apply(tmap, 0.01f, 1);
		dfsketches.terrain_generator.Window w = new Window(tmap);
		w.draw();
		ColorSolver tercol = new TerrainColorSolver();

		CosineGenerator wg = new CosineGenerator();
		int[][] wmap = wg.generate(256, worldgenParams);
		ColorSolver watcol = new WaterColorSolver();
		
		/*
		 * Adding new models to scene
		 */
		this.sc = scene;
		PolarCamera cam = new PolarCamera(800f, 600f, 100f);
//		Camera cam = new Camera(1000f, 1000f);
		sc.setActiveCamera(cam);
		sc.addModel(sc.getModel("terrain"), "water");
		sc.addModel((new TerrainConstructor(tmap, 1f, 1f, 1f, tercol)).construct(), "terrain");
		sc.addModel((new TerrainConstructor(wmap, 1f, 0.1f, 1f, watcol)).construct(), "water");
		sc.addModel(genPlayerModel(), "player");

		Matrix3D geoTransform = Matrix3D.getTranslationMatrix(-0.5f, 0f, -0.5f);
		geoTransform.multiplyRight(Matrix3D.getScaleMatrix(200f, 0.02f, 200f));
		geodata = new Geodata(tmap, geoTransform);
		
		nToMobs = new Hashtable<Long, MovableObject>();
		
		/*
		 * Adding static objects
		 */
		
		Instance terrInst = sc.addInstanceOfModel("terrain");
		terrInst.apply(geoTransform);
		Instance watInst = sc.addInstanceOfModel("water");
		watInst.apply(geoTransform);
		Instance skyInst = sc.addInstanceOfModel("sky");
		skyInst.apply(Matrix3D.getScaleMatrix(20f, 20f, 20f));

		MovableObject terrain = createMobForInst(terrInst);
		MovableObject sky = createMobForInst(skyInst);
		MovableObject water = createMobForInst(watInst);
		sky.addSubobject(terrain);
		sky.addSubobject(water);

//		MovableObject house1 = createMobForModel("house");
//		MovableObject door1 = createMobForModel("door");
//		house1.addSubobject(door1);
//		house1.moveTo(geodata.drop(new Vector3D(0f,0f,10f)));
//		door1.setActivator(new DoorActivation(door1));
//		terrain.addSubobject(house1);
				
//		Random forestRnd = new Random();
//		final int forestN = 2000;
//		Vector3D forestShift = new Vector3D(0f, -0.01f, 0f);
//		MovableObject[] forest = new MovableObject[forestN];
//		for (int i = 0; i < forestN; i++) {
//			Vector3D treePos = geodata.drop(new Vector3D(
//					forestRnd.nextFloat() * 100f - 50f,
//					0f,
//					forestRnd.nextFloat() * 100f - 50f));
//			treePos.add(forestShift);
//			if (treePos.getY() < 0) continue;
//			forest[i] = createMobForModel("tree");
//			forest[i].rotateY(forestRnd.nextFloat() * 10000f);
//			forest[i].scale(forestRnd.nextFloat() * 0.05f + 0.2f);
//			forest[i].moveTo(treePos);
//		}
		
		/*
		 * Preparing player
		 */
		player = new WanderingPlayer(cam, new Vector3D(0f, 0.8f, -1f), 0, this,
				createMobForModel("player"));
//		player = new Player(cam, new Vector3D(0f, 0.8f, -1f), 0);
	}
	
	public Player getPlayer() {
		return player;
	}
	
	protected MovableObject createMobForModel(String modelName) {
		MovableObject mob = new MovableObject(sc.addInstanceOfModel(modelName));
		nToMobs.put(mob.getNumber(), mob);
		return mob;
	}
	
	protected MovableObject createMobForInst(Instance i) {
		MovableObject mob = new MovableObject(i);
		nToMobs.put(mob.getNumber(), mob);
		return mob;
	}

	public MovableObject getMobWithN(long n) {
		return nToMobs.get(n);
	}

	public Geodata getGeodata() {
		return geodata;
	}
	
	private Model genPlayerModel() {
		BoneConstructor bc = new BoneConstructor();
//		Vertex[] v = bc.addBox(
//				new Vertex(-1f,-1f,-1f),
//				new Vector3D(2f,0f,0f),
//				new Vector3D(0f,2f,0f),
//				new Vector3D(0f,0f,2f)
//				);
		
		ModelConstructor mc = new ModelConstructor();
//		mc.addBox(0xFFFF0000, v);
		mc.setMainBone(bc.construct());
		
		return mc.construct();
	}

}